import org.scalatest.{FunSuite, BeforeAndAfter}

import akka.actor.{Actor, ActorRegistry}
import akka.actor.Actor._
import java.net.URLClassLoader
import java.net.URL

import edu.colorado.cs._

class MyProject extends Project {
  override def init_data() = {
    data += (1.0 -> None)
  }

  override def split_data(): Option[Double] = {
    data.find(_._2 match {
      case None => true
      case Some(d: Double) => false
    }) match {
      case None => None
      case Some(d: (Double, Option[Double])) => Some(d._1)
    }
  }

  override def recombine_data() = {
    //data foreach ((t) => println(t._1 + " -> " + t._2))
  }

  override def add_result(in: Double, out: Double) = {
    data += (in -> Some(out))
  }
}

class MyOtherProject extends Project {
  override def init_data() = {
  }

  override def split_data(): Option[Double] = {
    Some(1.0)
  }

  override def recombine_data() = {
  }

  override def add_result(in: Double, out: Double) = {
  }
}

class TestSuite extends FunSuite {
  val proj = new MyProject()
  Server.set_project(proj)
  Server.start(1337)

  test("Server won't allow project to be set twice") {
    val another_proj = new MyOtherProject()
    Server.set_project(another_proj)
    Server.get_project() match {
      case Some(p: MyProject) => assert(true)
      case Some(p: MyOtherProject) => fail("Returned wrong project.")
      case _ => fail("Something funky happened.")
    }
  }

  test("Server sends ProjectInfo class") {
    val actor = Actor.remote.actorFor("actor", "localhost", 1337)
    val urls = Array(new URL("http://localhost:1338/"))
    val cl = new URLClassLoader(urls, getClass().getClassLoader())
    var pi_class: Option[Class[ProjectInfo]] = None

    try {
      pi_class = Some(cl.loadClass("MyProjectInfo").asInstanceOf[Class[ProjectInfo]])
    } catch {
      case e: ClassNotFoundException => {
        fail("Caught ClassNotFoundException")
      }
    }

    val project_info: Option[ProjectInfo] = pi_class match {
      case Some(c: Class[ProjectInfo]) => Some(c.newInstance())
      case None => fail("No ProjectInfo class sent")
    }

    project_info match {
      case Some(p: ProjectInfo) => {
        //println(p.func(21.0))
        assert(true)
      }
      case None => fail("No instance of ProjectInfo")
    }
  }

  test("Server returns data") {
    val actor = Actor.remote.actorFor("actor", "localhost", 1337)

    actor !! GetData() match {
      case Some(message: Data) => message match {
        case Data(value: Double) => assert(true)
        case _ => fail("Data message didn't contain Double")
      }
      case None => fail("Server didn't send Data message")
    }
  }

  test("Server accepts results") {
    val actor = Actor.remote.actorFor("actor", "localhost", 1337)

    actor !! GetData() match {
      case Some(message: Data) => message match {
        case Data(value: Double) => actor ! Result(value, 42.0)
        case _ => fail("Data message didn't contain Double")
      }
      case None => fail("Server didn't send Data message")
    }
  }

  test("Server sends NoData message when out of data") {
    val actor = Actor.remote.actorFor("actor", "localhost", 1337)

    actor !! GetData() match {
      case Some(message: NoData) => message match {
        case NoData() => assert(true)
        case _ => fail("NoData message is funky.")
      }
      case Some(message: Data) => fail("Server sent data!")
      case None => fail("Server didn't send anything.")
    }
  }
}
